package com.cat.paxos.acceptor;

import com.cat.paxos.learner.HalfUpLeanerStrategy;
import com.cat.paxos.learner.LeanerStrategy;
import com.cat.paxos.learner.LearnProposalResponse;
import com.cat.paxos.learner.Learner;
import com.cat.paxos.proposal.Proposal;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @auther Cat.wang
 * @date 2020/1/16 16:16
 */
public class ObjectAcceptor implements Acceptor {

    /**
     * 提案学习者
     */
    private List<Learner> learners = new ArrayList<>();

    /**
     * 学习者策略
     */
    private LeanerStrategy strategy = new HalfUpLeanerStrategy();

    /**
     * 最大的提案编号
     */
    private String maxProposalNumber;

    /**
     * 当前选中的提案
     */
    private Proposal chooseProposal;

    @Override
    public String getProposalNumber() {
        return maxProposalNumber;
    }

    @Override
    public <T> ReceiveProposalResponse receive(Proposal<T> proposal) {

        // 校验提案编号是否小于于最大提案编号
        if (!strategy.decisionProposalNumberOk(maxProposalNumber, proposal.number())) {
            return new ReceiveProposalResponse() {

                private String proposalNumber = maxProposalNumber;

                @Override
                public String proposalNumber() {
                    return proposalNumber;
                }

                @Override
                public boolean ok() {
                    return false;
                }
            };
        }

        // 接收提案
        this.chooseProposal = proposal;
        this.maxProposalNumber = proposal.number();

        return new ReceiveProposalResponse() {

            private String proposalNumber = proposal.number();

            @Override
            public String proposalNumber() {
                return proposalNumber;
            }

            @Override
            public boolean ok() {
                return true;
            }
        };
    }

    @Override
    public ConfirmProposalResponse confirm(String proposalNumber) {

        // 校验是否能确认提案
        Proposal chooseProposal = this.chooseProposal;
        if (chooseProposal != null && !chooseProposal.number().equals(proposalNumber)) {
            return new ConfirmProposalResponse() {

                @Override
                public String proposalNumber() {
                    return maxProposalNumber;
                }

                @Override
                public boolean ok() {
                    return false;
                }
            };
        }
        // 学习者学习提案
        return this.strategy.decisionLeanProposal(
                this.learners.size(),
                (List<LearnProposalResponse>) this.learners.stream().parallel()
                        .map(l -> {
                            try {
                                return l.learn(chooseProposal);
                            } catch (Exception e) {
                                return null;
                            }
                        })
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList())
        );
    }

    @Override
    public List<Learner> learners() {
        return this.learners;
    }

    @Override
    public LeanerStrategy strategy() {
        return this.strategy;
    }

    public void setStrategy(LeanerStrategy strategy) {
        this.strategy = strategy;
    }
}
